(defun source-files (p-srcdir)
  "Return a list of java source files under
the given folder SRCDIR."
  (let ((rlst (list "")))
    (if (listp p-srcdir)
        (dolist (srcdir p-srcdir)
          (nconc rlst (source-files srcdir)))
      ;;(message "p-srcdir => %s" p-srcdir)
      (dolist (fname (directory-files p-srcdir 't))
        ;;(message "fname => %s" fname)
        (if (equal "." (substring fname -1)) ()
          (if (file-directory-p fname)
              (progn
                (if (directory-files fname nil "\\.java$")
                    (nconc rlst
                           (list (concat-file-names fname "*.java"))))
                (nconc rlst (source-files fname)))))))
    (cdr rlst))
  )

;;(source-files (list "c:/Temp/src1" "c:/Temp/src2"))
;;(source-files "c:/Temp/src2")

(defun string-from-list (list-of-string &optional sep)
  (concat (car list-of-string) 
          (if (cdr list-of-string)
              (concat sep (string-from-list (cdr list-of-string) sep)))))

(defun javac (&optional p-srcdir p-destdir p-cp &rest p-args)
  "Compile java source code specified in folder(s) P-SRCDIR.
P-SRCDIR could be a string specifying a single directory, or a list
of strings indicating multiple folders. P-DESTDIR specifies the
destination folder in which the class files are to be stored.
P-CP is the classpath, which could be a string or a list of strings.
P-ARGS are arguments given the javac command line.

Example: (javac \"/myproject/srcdir\" \"/myproject/build/classes\"
                (\"/lib1.jar\" \"/lib2/classes\") \"-g\")"
  (let ((options (list "")))
    (nconc options 
           (if p-destdir (list "-d" p-destdir))
           (if p-cp (list "-classpath"
                          (if (listp p-cp) (string-from-list p-cp ";") p-cp)))
           p-args
           (if p-srcdir (nconc options (source-files p-srcdir))))
    ;;options
    (new-proc-async (javac-executable)
                    (cdr options)
                    '((p-name . "*javac*")
                      (p-pop . t)))
    ))

(defun test-string-from-list ()
  (and
   (equal (string-from-list nil) "")
   (equal (string-from-list (list "a")) "a")
   (equal (string-from-list (list "a") ";") "a")
   (equal (string-from-list (list "a" "b" "c") ";") "a;b;c")
   (equal (string-from-list (list "a" "b" "c")) "abc")))
;;(test-string-from-list)

(defun java (&optional p-buffer-options
                       p-main-class
                       p-classpath
                       p-sysprops
                       &rest p-args)
  "Run a java program in asynchronous mode.
P-MAIN-CLASS is the qualified class name to be run;
P-CLASSPATH is the class path specified by -cp option.
It could be a single path, like c:/Temp
or a list of multiple paths, like '(\"c:/path1\", \"c:/path2\");
P-SYSPROPS is an associative list, i.e. (key . val),
specifying system properties,which would be passed to the
program in a form of \"-Dkey=val\" or \"-Xms512M\";
P-ARGS are arguments for the P-MAIN-CLASS."
  (new-proc-async (java-executable)
                  (java-options p-main-class
                                p-classpath
                                p-args
                                p-sysprops)
                  p-buffer-options))


(defun java-sync (&optional p-buffer-options
                            p-main-class
                            p-classpath
                            p-sysprops
                            &rest p-args)
  "Run a java program in synchronous mode.
Please refer to java-async for arguments."
  (new-proc-sync (java-executable)
                 (java-options p-main-class
                               p-classpath
                               p-args
                               p-sysprops)
                 p-buffer-options))


(defun java-sh (&optional p-buffer-options
                          p-main-class
                          p-classpath
                          p-sysprops
                          &rest p-args)
  (new-proc-sh (java-executable)
               (java-options p-main-class
                             p-classpath
                             p-args
                             p-sysprops)
               p-buffer-options)
  )
;;(java nil "Test" "c:/Temp" nil "Yang")
;;(java '((p-pop . 't) (p-name . "ym")) "Test" '("c:/Temp") nil "Yang Michael")


(defun jar-executable ()
  (_java-cmd "jar.exe"))

(defun java-executable ()
  (_java-cmd "java.exe"))

(defun javac-executable ()
  (_java-cmd "javac.exe"))

(defun _java-cmd (cmd)
  (concat-file-names (java-home) "/bin" cmd))

(defun java-home ()
  (let ((j-home (getenv "JAVA_HOME")))
    (if (not j-home) (error "Please define JAVA_HOME") j-home)
    ))

(defun java-options (&optional p-main-class
                               p-classpath
                               p-args
                               p-sysprops)
  (let ((options (cons "" nil)))
    (if p-classpath
        (nconc options
               (list "-cp"
                     (if (listp p-classpath)
                         (list-to-string p-classpath ";")
                       p-classpath))))
    
    (dolist (sysprop p-sysprops)
      (nconc options
             (list (if (stringp sysprop) sysprop
                     (concat "-D" (car sysprop) "=" (cdr sysprop)))
                   )))

    (nconc options (list p-main-class))

    (dolist (arg p-args)
      (if arg (nconc options (list arg))))

    (cdr options))
  )


(defun test-java-options ()
  (and (equal '("-cp" "A;B;C" "-Dk1=v1" "-Dk2=v2"
                "abc" "arg1" "arg2" "arg3")
              (java-options "abc" '("A" "B" "C")
                            (list "arg1" "arg2" "arg3")
                            '(("k1" . "v1") ("k2" . "v2"))))

       (equal '("-cp" "A;B;C" "-Xmx512M" "abc" "arg1" "arg2" "arg3")
              (java-options "abc" '("A" "B" "C")
                            (list "arg1" "arg2" "arg3")
                            '("-Xmx512M")))

       (equal '("-cp" "A;B;C" "-Dk1=v1" "-Xms128M" "-Dk2=v2"
                "-Xmx512M" "abc" "arg1" "arg2" "arg3")
              (java-options "abc" '("A" "B" "C")
                            (list "arg1" "arg2" "arg3")
                            '(("k1" . "v1") "-Xms128M" ("k2" . "v2")
                              "-Xmx512M")))


       (equal '("-cp" "A;B;C" "abc")
              (java-options "abc" '("A" "B" "C")))

       (equal '("abc") (java-options "abc"))
       )
  )


;;(test-java-options)

;;(java-sync '((p-clear . t) (p-readonly . t) (p-name . "abc"))
;;"abc" '("A" "B" "C") '(("k1" . "v1") ("k2" . "v2")) ;;"arg1" "arg2" "arg3")


;;(java-async '((p-clear . t) (p-readonly . t) (p-name . "abc"))
;;"abc" '("A" "B" "C") '(("k1" . "v1") ("k2" . "v2")) ;;"arg1" "arg2" "arg3")


(defun ant-run (&optional build-file &rest targets)
  (interactive)
  (apply 'java
         '((p-name . "*ant*")
           (p-pop . t) (p-clear . t)
           (p-readonly . t))
         "org.apache.tools.ant.launch.Launcher"
         (ant-launcher-jar)
         (list (cons "ant.home" (ant-home)))
         "-e" "-f"
         (if (= 0 (length build-file)) "build.xml" build-file) targets))

(defun ant-launcher-jar ()
  (list (concat-file-names (ant-home) "lib/ant-launcher.jar")))

(defun ant-home ()
  (get-env "ANT_HOME"))


(defun locate-java-class (start-directory class-name)
  "Find the jar file containing the class file of the given CLASS-NAME
in the given START-DIRECTORY and its sub-directories."
  (let ((jar) (matcher
         (lambda (fname)
           (if (not (string-match "\\.jar$" fname)) nil
             (unzip nil fname "-l")             
             (if (catch 'tag
                   (with-current-buffer "*unzip*"
                     (dolist (each-line (split-string (buffer-string) "\n"))
                       (if (string-match (concat class-name "$")
                                         each-line)
                           (progn (kill-buffer)
                                  (throw 'tag 't))))
                     (kill-buffer) nil
                     ))
                 (throw 'jar fname))))))
    (setq jar (catch 'jar
                (dolist (jar-file
                         (directory-files-recursively-2
                          start-directory
                          't  matcher))))) 
    (insert  jar)))

(defun ant-doc ()
  )